package ogmios.dialogs;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.util.List;
import javax.swing.text.*;
import javax.swing.text.rtf.*;
import java.util.*;
import java.util.regex.*;
import javax.swing.JSpinner.*;
import java.text.*;
import javax.swing.tree.*;
import ogmios.core.*;
import ogmios.components.*;
import ogmios.components.templatetags.*;
/**
 * This class provides an interface that allows a user
 * to create new Files based on a pre-defined Template.
 */
public class TemplateManager extends JDialog
{
 private Map<String, String> templates = new LinkedHashMap<String, String>();
 private JTextArea preview;
 private JCheckBox closeAfterCreation;
 private JComboBox templateSelections;
 private Set<TemplateTag> templateTags;

 private DefaultMutableTreeNode hiveNode;

 public static final Set<TemplateTag> DEFAULT_TEMPLATE_TAGS;
 public static final TemplateTag UNEXTENDED_FILE_NAME_TAG;
 public static final TemplateTag DESCRIPTION_TAG;
 public static final TemplateTag LINE_TAG;
 public static final TemplateTag MONTH_TAG;
 public static final TemplateTag DAY_TAG;
 public static final TemplateTag YEAR_TAG;
 public static final TemplateTag DAY_OF_WEEK_TAG;
 public static final TemplateTag TIME_TAG;
 static
 {
  DEFAULT_TEMPLATE_TAGS = new HashSet<TemplateTag>();
  DEFAULT_TEMPLATE_TAGS.add(UNEXTENDED_FILE_NAME_TAG=new FileTag());
  DEFAULT_TEMPLATE_TAGS.add(DESCRIPTION_TAG=new UserInputTag("{description}", "Enter a description:", UserInputTag.MULTI_LINE));
  DEFAULT_TEMPLATE_TAGS.add(LINE_TAG=new UserInputTag("{line}", "Enter a line:", UserInputTag.ONE_LINE));
  DEFAULT_TEMPLATE_TAGS.add(MONTH_TAG=new DateTag("{month}", DateTag.MONTH));
  DEFAULT_TEMPLATE_TAGS.add(DAY_TAG=new DateTag("{day}", DateTag.DAY));
  DEFAULT_TEMPLATE_TAGS.add(YEAR_TAG=new DateTag("{year}", DateTag.YEAR));
  DEFAULT_TEMPLATE_TAGS.add(DAY_OF_WEEK_TAG=new DateTag("{day of week}", DateTag.DAY_OF_WEEK));
  DEFAULT_TEMPLATE_TAGS.add(TIME_TAG=new DateTag("{time}", DateTag.TIME));
 }
 public static void main(String[] args) throws Throwable
 {
  UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  new TemplateManager().showTemplateManager(null,DEFAULT_TEMPLATE_TAGS);
 }

 public TemplateManager()
 {
  this("templates");
 }
 
 public TemplateManager(String templatePath)
 {
  this(templatePath, null);
 }

 public TemplateManager(String templatePath, Frame owner)
 {
   super(owner);
   setTitle("Template Manager");
   templateSelections = new JComboBox();
   preview=new JTextArea();
   preview.setEditable(false);
   JButton createNewFile = new JButton("Create New File");
   closeAfterCreation = new JCheckBox("Close After Creation");
   setModal(true);
   Toolkit toolkit = Toolkit.getDefaultToolkit();
   Action createTemplateFile =
     new AbstractAction()
     {
      public void actionPerformed(ActionEvent e)
      {
        if (!templateSelections.isPopupVisible() && templateSelections.getItemCount() > 0)
        {

        String templateName = (String)templateSelections.getSelectedObjects()[0];
        String extension = "." + OgmiosUtilities.fileNameExtension(templateName); 
         JFileChooser fileDialog = OgmiosFileChooser.getFileDialog(extension.length() > 0 ? extension + " | " + "\"" + extension + " Template\"": null);
         fileDialog.setMultiSelectionEnabled(false);   
         fileDialog.setDialogTitle("Create New " + templateName);
         if (fileDialog.showSaveDialog(TemplateManager.this)==JFileChooser.APPROVE_OPTION)
         {
          Ogmios.OgmiosNode node = modifiedTemplateNode(templates.get(templateName), fileDialog.getSelectedFile(), templateTags);
          if (node != null)
          {
           node.save();
           Ogmios.ogmiosGUI.treeModel.insertNodeInto(node, hiveNode, hiveNode.getChildCount());
           Ogmios.ogmiosGUI.openDocument(node);
           if (closeAfterCreation.isSelected())
           {
             setVisible(false);
           }
          }
        }
        }
  
      }
     };
   createNewFile.addActionListener(createTemplateFile);
   OgmiosUtilities.closeableChild(getRootPane());
   templateSelections.getInputMap(JComponent.WHEN_FOCUSED).put(
    KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,toolkit.getMenuShortcutKeyMask()), "createNewFile");
   templateSelections.getActionMap().put("createNewFile", createTemplateFile);
   templateSelections.addItemListener(
    new ItemListener()
   { 
    public void itemStateChanged(ItemEvent e)
    {
      if (e.getStateChange() == ItemEvent.SELECTED)
      {
        preview.setText(
         templates.get(
          templateSelections.getSelectedObjects()[0]
         )
        );
        preview.setSelectionStart(0);
        preview.setSelectionEnd(0);
      }
      else
      {
        preview.setText("");
      }
    } 
   }
   );

   Container contents = getContentPane();
   contents.add(templateSelections, BorderLayout.NORTH);
   contents.add(new JScrollPane(preview));
   JPanel dualPanel = new JPanel(new BorderLayout());
   dualPanel.add(closeAfterCreation, BorderLayout.NORTH);
   dualPanel.add(createNewFile);
   contents.add(dualPanel, BorderLayout.SOUTH);
   Dimension screenSize = toolkit.getScreenSize();
   screenSize.width /= 3.8;
   screenSize.height /= 2;
   setSize (screenSize);
   populate(templatePath);
 }

 /**
 * Shows TemplateManager.
 * @param     hiveNode     The parent node to insert any created Templates into
 */
 public void showTemplateManager(DefaultMutableTreeNode hiveNode)
 {
  showTemplateManager(hiveNode, DEFAULT_TEMPLATE_TAGS);
 }
 /**
 * Shows TemplateManager.
 * @param     hiveNode     The parent node to insert any created Templates into
 * @param     templateTags     The set of TemplateTags to use to parse the Templates
 */
 public void showTemplateManager(DefaultMutableTreeNode hiveNode, Set<TemplateTag> templateTags)
 {
  showTemplateManager(Ogmios.ogmiosGUI, hiveNode, templateTags);
 }

 /**
 * Shows TemplateManager.
 * @param     host     The component to launch the TemplateManager relative to
 * @param     hiveNode     The parent node to insert any created Templates into
 * @param     templateTags     The set of TemplateTags to use to parse the Templates
 */
 public void showTemplateManager(Component host, DefaultMutableTreeNode hiveNode, Set<TemplateTag> templateTags)
 {
   this.hiveNode = hiveNode;
   this.templateTags = templateTags;
   setLocationRelativeTo(host);
   setVisible(true);
   this.hiveNode = null;
 }

 /**
 * Fill the TemplateManager with all files found in the given folder
 */
 private void populate(String templatePath)
 {
  File templatesFolder = new File(templatePath);
  if (templatesFolder.exists())
  {
   for (File templateFile : templatesFolder.listFiles())
   {
     if (templateFile.isFile())
     {
      addTemplate(templateFile);
     }
   }
  }
 }
 
 /**
 * Returns the checkbox that sets whether the TemplateManager should close after creating a template
 * <br>
 * Note that this was mostly made out of convenience for the Ogmios class and should be used sparingly, as it is a likely candidate
 * for replacement and deprecation in the future.
 */
 public JCheckBox getCloseAfterCreationCheckBox()
 {
  return closeAfterCreation;
 }
 /**
 * Returns whether the TemplateManager will close after creating a template
 */
 public boolean getCloseAfterCreation()
 {
  return closeAfterCreation.isSelected();
 }
 /**
 * Set whether the TemplateManager will close after creating a template
 */
 public void setCloseAfterCreation(boolean close)
 {
  closeAfterCreation.setSelected(close);
 }

 /**
 * Returns an OgmiosNode containing a template based on the given file, or null if there is an error creating the node.
 * @param     template     The basic template to use
 * @param     outputFile     The file where this template would be saved
 * @param     templateTags     The set of TemplateTags to use to parse the template
 */
 public static Ogmios.OgmiosNode modifiedTemplateNode(String template, File outputFile, Set<TemplateTag> templateTags)
 {
  try
  {
   String path = outputFile.getCanonicalPath();
   return Ogmios.ogmiosGUI.createNode(path, 
     modifiedTemplate(template, outputFile, templateTags));
  }
  catch(IOException ex)
  {
   OgmiosUtilities.showErrorBox(null, ex, "Template:Create Error");
   return null;
  }
 }
 
 /**
 * Returns a modified template based on the given file
 * @param     template     The basic template to use
 * @param     outputFile     The file where this template would be saved
 * @param     templateTags     The set of TemplateTags to use to parse the template
 */
 public static String modifiedTemplate(String template, File outputFile, Set<TemplateTag> templateTags)
 {
  Map<String, TemplateTag> tags = new HashMap<String, TemplateTag>();
  Map<String, String> values = new HashMap<String, String>();
  StringBuffer regexBuilder = new StringBuffer();
  for (TemplateTag templateTag : templateTags)
  {
   String tag = templateTag.getTag();
   tags.put(tag, templateTag);
   regexBuilder.append("(").append(Pattern.quote(tag)).append(")|");
  }
  int regexBuilderLength = regexBuilder.length();
  String regex = regexBuilderLength != 0 ? regexBuilder.substring(0, regexBuilderLength - 1) : regexBuilder.toString();
  Matcher matcher = Pattern.compile(regex).matcher(template);
  regexBuilder.setLength(0);
  while(matcher.find())
  {
   String tag = matcher.group();
   String value = values.get(tag);
   if (value == null)
   {
    values.put(tag, value=Matcher.quoteReplacement(tags.get(tag).getValue(outputFile)));
   }
   matcher.appendReplacement(regexBuilder, value);
  }
  matcher.appendTail(regexBuilder);
  return regexBuilder.toString();
 }

 /**
 * Adds template at templateFile to this TemplateManager
 */
 public void addTemplate(File templateFile)
 {
   if (templateFile.exists())
   {
    try
    {
      addTemplate(OgmiosUtilities.getFileName(templateFile.getCanonicalPath()),
                           OgmiosUtilities.plainFileContents(templateFile));  
     }
     catch(Throwable ex)
     {
        OgmiosUtilities.showErrorBox(TemplateManager.this, ex, "Template:Load Error");
      }
   }
 }

 /**
 * Adds template to this TemplateManager
 * @param     templateName     The name of the template
 * @param     templateContents      The template code
 */
 public void addTemplate(String templateName, String templateContents)
 {
  templates.put(templateName, templateContents);
  templateSelections.addItem(templateName);
 }

 /**
 * Removes template from TemplateManager
 */
 public void removeTemplate(String templateName)
 {
  String templateContents = templates.get(templateName);
  if (templateContents != null)
  {
   templateSelections.removeItem(templateContents);
   templates.remove(templateName);
  }
 }

}
